package cn.dengta.context.web;

import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

import cn.dengta.common.model.TimeInterval;
import cn.dengta.common.security.AbuseDefender;
import cn.dengta.context.model.*;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.*;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 * 用来限制接口访问的频率，数据范围
 */
@Slf4j
@Aspect
@Component
public class SafeLimitInterceptor {

	private final ConcurrentHashMap<String, FrequencyLimitAbuseDefender<Member>> defenderMap = new ConcurrentHashMap<>(32);

	@Pointcut("@annotation(cn.dengta.context.web.Limit)")
	public void actionMethodPointcut() {
	}

	@Before("actionMethodPointcut()")
	public void doBefore(JoinPoint joinPoint) {
		handle(joinPoint);
	}

	private void handle(JoinPoint joinPoint) {
		Method method = ((MethodSignature) (joinPoint.getSignature())).getMethod();
		Limit limit = method.getAnnotation(Limit.class);
		ProxyRequest proxyRequest = findProxyRequest(joinPoint);
		if (limit != null && limit.internal() > 0 && proxyRequest != null) {
			TimeInterval interval = proxyRequest.getInterval();
			if (interval == null) {
				interval = new TimeInterval(new Date(), new Date());
			}
			Date begin = X.expectNotNull(interval.getBegin(), new Date());
			Date end = X.expectNotNull(interval.getEnd(), new Date());
			Calendar endCalendar = new EasyDate(begin).getCalendar();
			endCalendar.add(limit.unit(), limit.internal());
			Date expectEnd = endCalendar.getTime();
			Assert.state(!end.after(expectEnd), "只能查" + DateUnit.ofField(limit.unit()).getDesc(limit.internal()) + "内的数据");
		}
		if (limit != null && proxyRequest != null) {
			Member member = proxyRequest.getSessionUser();
			if (member == null) {
				member = new NullMember();
			}
			if (limit.accessFrequency() > 0 && !proxyRequest.isExport()) {
				FrequencyLimitAbuseDefender<Member> defender = defenderMap.computeIfAbsent(getFullName(method), key -> new FrequencyLimitAbuseDefender<>(
						new AbuseDefender.Threshold(1, limit.accessFrequency() * 1000L),
						Member::encodedId));
				defender.assertThrowing(member);
			}
			if (limit.exportFrequency() > 0 && proxyRequest.isExport()) {
				FrequencyLimitAbuseDefender<Member> defender = defenderMap.computeIfAbsent("export-" + getFullName(method), key -> new FrequencyLimitAbuseDefender<>(
						new AbuseDefender.Threshold(1, limit.exportFrequency() * 1000L), m -> "export-" + m.encodedId()));
				defender.assertThrowing(member);
			}
		}
	}

	private String getFullName(Method method) {
		return method.getDeclaringClass().getCanonicalName() + "." + method.getName();
	}

	public static ProxyRequest findProxyRequest(JoinPoint joinPoint) {
		Object[] args = ArrayUtil.filter(joinPoint.getArgs(), e -> e instanceof ProxyRequest);

		if (args.length <= 0) {
			return null;
		}
		return (ProxyRequest) args[0];
	}

	public static class NullMember implements Member {

		@Override
		public Long getId() {
			return 0L;
		}

		@Override
		public void setId(Long id) {

		}

		@Override
		public boolean hasPermission(String code, String routeCode, boolean checkRouteCode) {
			return false;
		}

		@Override
		public boolean isFrozen() {
			return false;
		}

		@Override
		public boolean canLogin() {
			return false;
		}

		@Override
		public boolean canLoginBackstage() {
			return false;
		}

		@Override
		public void setSessionId(String sessionId) {

		}

		@Override
		public boolean isDestroyed() {
			return false;
		}

		@Override
		public String getUsername() {
			return null;
		}

		@Override
		public Language getLanguage() {
			return null;
		}

		@Override
		public String getPassword() {
			return null;
		}

		@Override
		public FreezeTip getFreezeTip() {
			return null;
		}

	}

}
